/*******************************************************************************
*
*   FILE NAME:
*       main.cpp
*
*   DESCRIPTION:
*       Contains main method and OpenGL setup
*
*******************************************************************************/
#include <Windows.h>
#include <gl\GL.h>
#include <gl\GLU.h>

// function prototypes
LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
bool CreateGameWindow( void );
bool DestroyGameWindow( void );
bool InitGraphics( void );

/*----------------------------------------------------------
WinMain
----------------------------------------------------------*/
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
    // register window class
    WNDCLASSEX wc;
    wc.cbSize        = sizeof( WNDCLASSEX );
    wc.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wc.lpfnWndProc   = WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = GetModuleHandle( NULL );
    wc.hIcon         = LoadIcon( NULL, IDI_WINLOGO );
    wc.hCursor       = LoadCursor( NULL, IDC_ARROW );
    wc.hbrBackground = NULL;
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = L"GameWindowClass";
    wc.hIconSm       = LoadIcon( NULL, IDI_APPLICATION );
    if( !RegisterClassEx( &wc ) )
    {
        MessageBox( NULL, L"Window Registration Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
        return 0;
    }

    // calculate client frame size
    RECT rect = { 0, 0, 800, 600 };
    DWORD dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
    DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
    AdjustWindowRect( &rect, dwStyle, FALSE );

    // create window
    HWND hWnd = CreateWindowEx
    (
        dwExStyle,
        wc.lpszClassName,
        L"Game Demo",
        dwStyle,
        CW_USEDEFAULT, CW_USEDEFAULT,
        rect.right - rect.left, rect.bottom - rect.top,
        NULL, NULL, wc.hInstance, NULL
    );
    if( hWnd == NULL )
    {
        MessageBox( NULL, L"Window Creation Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
        return 0;
    }

    // set up device and rendering contexts
    PIXELFORMATDESCRIPTOR pfd =
    {
        sizeof( PIXELFORMATDESCRIPTOR ),
        1,
        PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
        PFD_TYPE_RGBA,
        16,
        0, 0, 0, 0, 0, 0,
        0,
        0,
        0,
        0, 0, 0, 0,
        16,
        0,
        0,
        PFD_MAIN_PLANE,
        0,
        0, 0, 0
    };
    HDC hDC;
    HGLRC hRC;
    GLuint PixelFormat;
    if( !( hDC = GetDC( hWnd ) ) )
    {
        MessageBox( NULL, L"Device Context Creation Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
        if( hRC )
        {
            if( !wglMakeCurrent( NULL, NULL ) )
            {
                MessageBox( NULL, L"Device Context and Rendering Context Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            }
            if( !wglDeleteContext( hRC ) )
            {
                MessageBox( NULL, L"Rendering Context Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            }
            hRC = NULL;
        }
        if( hDC && !ReleaseDC( hWnd, hDC ) )
        {
            MessageBox( NULL, L"Device Context Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            hDC = NULL;
        }
        if( hWnd && !DestroyWindow( hWnd ) )
        {
            MessageBox( NULL, L"Window Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            hWnd = NULL;
        }
        if( !UnregisterClass( wc.lpszClassName, wc.hInstance ) )
        {
            MessageBox( NULL, L"Window Unregistration Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            wc.hInstance = NULL;
        }
    }
    if( !( PixelFormat = ChoosePixelFormat( hDC, &pfd ) ) )
    {
        MessageBox( NULL, L"No Matching Pixel Format Found.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
        if( hRC )
        {
            if( !wglMakeCurrent( NULL, NULL ) )
            {
                MessageBox( NULL, L"Device Context and Rendering Context Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            }
            if( !wglDeleteContext( hRC ) )
            {
                MessageBox( NULL, L"Rendering Context Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            }
            hRC = NULL;
        }
        if( hDC && !ReleaseDC( hWnd, hDC ) )
        {
            MessageBox( NULL, L"Device Context Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            hDC = NULL;
        }
        if( hWnd && !DestroyWindow( hWnd ) )
        {
            MessageBox( NULL, L"Window Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            hWnd = NULL;
        }
        if( !UnregisterClass( wc.lpszClassName, wc.hInstance ) )
        {
            MessageBox( NULL, L"Window Unregistration Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            wc.hInstance = NULL;
        }
    }
    if( !SetPixelFormat( hDC, PixelFormat, &pfd ) )
    {
        MessageBox( NULL, L"Unable To Set Pixel Format.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
        if( hRC )
        {
            if( !wglMakeCurrent( NULL, NULL ) )
            {
                MessageBox( NULL, L"Device Context and Rendering Context Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            }
            if( !wglDeleteContext( hRC ) )
            {
                MessageBox( NULL, L"Rendering Context Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            }
            hRC = NULL;
        }
        if( hDC && !ReleaseDC( hWnd, hDC ) )
        {
            MessageBox( NULL, L"Device Context Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            hDC = NULL;
        }
        if( hWnd && !DestroyWindow( hWnd ) )
        {
            MessageBox( NULL, L"Window Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            hWnd = NULL;
        }
        if( !UnregisterClass( wc.lpszClassName, wc.hInstance ) )
        {
            MessageBox( NULL, L"Window Unregistration Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            wc.hInstance = NULL;
        }
    }
    if( !( hRC = wglCreateContext( hDC ) ) )
    {
        MessageBox( NULL, L"Rendering Context Creation Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
        if( hRC )
        {
            if( !wglMakeCurrent( NULL, NULL ) )
            {
                MessageBox( NULL, L"Device Context and Rendering Context Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            }
            if( !wglDeleteContext( hRC ) )
            {
                MessageBox( NULL, L"Rendering Context Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            }
            hRC = NULL;
        }
        if( hDC && !ReleaseDC( hWnd, hDC ) )
        {
            MessageBox( NULL, L"Device Context Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            hDC = NULL;
        }
        if( hWnd && !DestroyWindow( hWnd ) )
        {
            MessageBox( NULL, L"Window Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            hWnd = NULL;
        }
        if( !UnregisterClass( wc.lpszClassName, wc.hInstance ) )
        {
            MessageBox( NULL, L"Window Unregistration Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            wc.hInstance = NULL;
        }
    }
    if( !wglMakeCurrent( hDC, hRC ) )
    {
        MessageBox( NULL, L"Unable to Set Rendering Context.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
        if( hRC )
        {
            if( !wglMakeCurrent( NULL, NULL ) )
            {
                MessageBox( NULL, L"Device Context and Rendering Context Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            }
            if( !wglDeleteContext( hRC ) )
            {
                MessageBox( NULL, L"Rendering Context Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            }
            hRC = NULL;
        }
        if( hDC && !ReleaseDC( hWnd, hDC ) )
        {
            MessageBox( NULL, L"Device Context Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            hDC = NULL;
        }
        if( hWnd && !DestroyWindow( hWnd ) )
        {
            MessageBox( NULL, L"Window Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            hWnd = NULL;
        }
        if( !UnregisterClass( wc.lpszClassName, wc.hInstance ) )
        {
            MessageBox( NULL, L"Window Unregistration Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
            wc.hInstance = NULL;
        }
    }

    // show window
    ShowWindow( hWnd, SW_SHOW );
    SetForegroundWindow( hWnd );
    SetFocus( hWnd );

    // set up initial matrix info
    glViewport( 0, 0, rect.right, rect.bottom );
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    gluPerspective( 45.0f, (float)rect.right/(float)rect.bottom, 0.1f, 100.0f );
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();

    // set up openGL
    glShadeModel( GL_SMOOTH );
    glClearColor( 0.0f, 0.0f, 0.0f, 0.5f );
    glClearDepth( 1.0f );
    glEnable( GL_DEPTH_TEST );
    glDepthFunc( GL_LEQUAL );
    glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
    
    // enter event message loop
    MSG msg;
    while( TRUE )
    {
        // handle any messages
        if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
        {
            if( msg.message == WM_QUIT )
                break;

            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }

        // draw scene at 60 fps max
        static DWORD prev = GetTickCount();
        DWORD curr = GetTickCount();
        if( (float)( curr - prev ) > 1000.0f/60.0f )
        {
            glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
            glMatrixMode( GL_MODELVIEW );
            glLoadIdentity();
            SwapBuffers(hDC);
            prev = curr;
        }
    }

    // clean up device and rendering contexts
    if( hRC )
    {
        if( !wglMakeCurrent( NULL, NULL ) )
        {
            MessageBox( NULL, L"Device Context and Rendering Context Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
        }
        if( !wglDeleteContext( hRC ) )
        {
            MessageBox( NULL, L"Rendering Context Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
        }
        hRC = NULL;
    }
    if( hDC && !ReleaseDC( hWnd, hDC ) )
    {
        MessageBox( NULL, L"Device Context Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
        hDC = NULL;
    }
    if( hWnd && !DestroyWindow( hWnd ) )
    {
        MessageBox( NULL, L"Window Release Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
        hWnd = NULL;
    }
    if( !UnregisterClass( wc.lpszClassName, wc.hInstance ) )
    {
        MessageBox( NULL, L"Window Unregistration Failed.", L"ERROR", MB_ICONEXCLAMATION | MB_OK );
        wc.hInstance = NULL;
    }

    // exit
    return (int) msg.wParam;
}

/*----------------------------------------------------------
WndProc
----------------------------------------------------------*/
LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
        // handle key press/release
        case WM_KEYDOWN:
        {
            switch( wParam )
            {
                case VK_ESCAPE:
                {
                    PostQuitMessage( 0 );
                    break;
                }
            }
            return 0;
        }
        case WM_KEYUP:
        {
            return 0;
        }

        // handle window resize
        case WM_SIZE:
        {
            return 0;
        }

        // handle window active state change
        case WM_ACTIVATE:
        {
            // check active state
            if( !HIWORD( wParam ) )
            {
                // active
            }
            else
            {
                // not active
            }
            return 0;
        }

        // handle system commands
        case WM_SYSCOMMAND:
        {
            switch( wParam )
            {
                // handle screensaver and monitor power save mode
                case SC_SCREENSAVE:
                case SC_MONITORPOWER:
                {
                    return 0;
                }
            }
            break;
        }

        // handle shutting down
        case WM_CLOSE:
        {
            PostQuitMessage(0);
            return 0;
        }
    }

    return DefWindowProc( hWnd, msg, wParam, lParam );
}
